package sf.dsl.example;

import sf.common.exception.SmallOrmException;
import sf.core.DBField;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * 和QTable类似,支持基本查询条件
 */
public class Criteria {

    //值是否不能为空
    protected boolean notNull;

    protected List<ICondition> list;

    protected List<SimpleTable> fromTables = Collections.emptyList();

    protected Object temp;

    protected SimpleTable simpleTable(DBField property) {
        if (property != null) {
            return SimpleTable.create(property);
        }
        return null;
    }

    protected SimpleField simpleField(DBField property) {
        return DSLMethod.simpleField(fromTables, property);
    }

    private DBField property(DBField property) {
        return property;
    }

    protected ICondition addCriterion(String condition) {
        return QPI.addCondition(condition);
    }

    /**
     * 自定义条件
     * @param condition 例如 "length(countryname)<5"
     * @return
     */
    public Criteria addCondition(String condition) {
        ICondition c = addCriterion(condition);
        list.add(c);
        return this;
    }

    /**
     * 1=1
     * @return
     */
    public Criteria trueCondition() {
        list.add(QPI.trueCondition());
        return this;
    }

    /**
     * 1=0
     * @return
     */
    public Criteria falseCondition() {
        list.add(QPI.falseCondition());
        return this;
    }

    /**
     * 自定义左边条件，右边用value值
     * @param condition 例如 "length(countryname)="
     * @param value     例如 5
     * @return
     */
    public Criteria addCondition(String condition, Object value) {
        list.add(QPI.addCondition(condition, value));
        return this;
    }

    /**
     * 自定义左边条件，右边用value值,支持? 参数,框架能自动替换,注意?和集合个数需要一一对应.
     * @param condition
     * @param values
     * @return
     */
    public Criteria addCondition(String condition, Collection<?> values) {
        ICondition c = addCriterion(condition, values);
        list.add(c);
        return this;
    }

    protected ICondition addCriterion(DBField property, DBOperator operator) {
        if (property == null) {
            return QPI.trueCondition();
        }
        SimpleField simpleField = simpleField(property);
        return QPI.addCondition(simpleField, operator);
    }

    protected ICondition addCriterion(String condition, Collection<?> values) {
        if (isNull(null, values)) {
            return QPI.trueCondition();
        }
        return QPI.addCondition(condition, values);
    }

    protected ICondition addCriterion(DBField property, DBOperator operator, Object value) {
        if (isNull(property, value)) {
            return QPI.trueCondition();
        }
        if (property == null) {
            return QPI.trueCondition();
        }
        SimpleField simpleField = simpleField(property);
        return QPI.addCondition(simpleField, operator, value);
    }

    /**
     * @param operator
     * @param value
     * @param property
     * @param defaultValue 填充相等或不相等条件,true是,false为否
     * @return
     */
    protected ICondition addCriterionArray(DBField property, DBOperator operator, Object value, boolean defaultValue) {
        if (isNull(property, value)) {
            return QPI.trueCondition();
        }
        if (property == null) {
            return QPI.trueCondition();
        }
        SimpleField simpleField = simpleField(property);
        return QPI.addConditionArray(simpleField, operator, value, defaultValue);
    }

    private boolean isNull(DBField property, Object value) {
        if (value == null) {
            if (notNull) {
                throw new SmallOrmException("Value for " + property + " cannot be null");
            } else {
                return true;
            }
        }
        return false;
    }

    protected ICondition addCriterionBetween(DBField property, DBOperator operator, Object value1, Object value2) {
        if (value1 == null || value2 == null) {
            if (notNull) {
                throw new SmallOrmException("Between values for " + property + " cannot be null");
            } else {
                return QPI.trueCondition();
            }
        }
        if (property == null) {
            return QPI.trueCondition();
        }
        SimpleField simpleField = simpleField(property);
        return QPI.addConditionBetween(simpleField, operator, value1, value2);
    }

    //以下是 DBField 条件

    public Criteria isNull(DBField property) {
        ICondition c = addCriterion(property(property), DBOperator.IS_NULL);
        list.add(c);
        return this;
    }

    public Criteria isNotNull(DBField property) {
        ICondition c = addCriterion(property(property), DBOperator.IS_NOT_NULL);
        list.add(c);
        return this;
    }

    public Criteria eq(DBField property, Object value) {
        ICondition c = addCriterion(property(property), DBOperator.EQUALS, value);
        list.add(c);
        return this;
    }

    public Criteria ne(DBField property, Object value) {
        ICondition c = addCriterion(property(property), DBOperator.NOT_EQUALS, value);
        list.add(c);
        return this;
    }

    public Criteria gt(DBField property, Object value) {
        ICondition c = addCriterion(property(property), DBOperator.GREAT, value);
        list.add(c);
        return this;
    }

    public Criteria ge(DBField property, Object value) {
        ICondition c = addCriterion(property(property), DBOperator.GREAT_EQUALS, value);
        list.add(c);
        return this;
    }

    public Criteria lt(DBField property, Object value) {
        ICondition c = addCriterion(property(property), DBOperator.LESS, value);
        list.add(c);
        return this;
    }

    public Criteria le(DBField property, Object value) {
        ICondition c = addCriterion(property(property), DBOperator.LESS_EQUALS, value);
        list.add(c);
        return this;
    }

    public Criteria in(DBField property, Object value) {
        // 对于空集合的IN条件为永假
        ICondition c = null;
        if (value == null) {
            c = QPI.falseCondition();
        } else {
            c = addCriterion(property(property), DBOperator.IN, value);
        }
        list.add(c);
        return this;
    }

    public Criteria in(DBField property, Collection<?> values) {
        // 对于空集合的IN条件为永假
        ICondition c = addCriterionArray(property(property), DBOperator.IN, values, false);
        list.add(c);
        return this;
    }

    public Criteria in(DBField property, Object... values) {
        // 对于空集合的IN条件为永假
        ICondition c = addCriterionArray(property(property), DBOperator.IN, values, false);
        list.add(c);
        return this;
    }

    public Criteria in(DBField[] fields, Collection<?> values) {
        SimpleField[] simpleFields = new SimpleField[fields.length];
        for (int i = 0; i < fields.length; i++) {
            simpleFields[i] = simpleField(fields[i]);
        }
        ICondition c = QPI.in(simpleFields, values);
        list.add(c);
        return this;
    }

    public Criteria notIn(DBField property, Object value) {
        // 对于空集合的IN条件为永真
        ICondition c = null;
        if (value == null) {
            c = QPI.trueCondition();
        } else {
            c = addCriterion(property(property), DBOperator.NOT_IN, value);
        }
        list.add(c);
        return this;
    }

    public Criteria notIn(DBField property, Collection<?> values) {
        // 对于空集合的NOT IN条件为永真
        ICondition c = addCriterionArray(property(property), DBOperator.NOT_IN, values, true);
        list.add(c);
        return this;
    }

    public Criteria notIn(DBField property, Object... values) {
        // 对于空集合的NOT IN条件为永真
        ICondition c = addCriterionArray(property(property), DBOperator.NOT_IN, values, true);
        list.add(c);
        return this;
    }

    public Criteria notIn(DBField[] fields, Collection<?> values) {
        SimpleField[] simpleFields = new SimpleField[fields.length];
        for (int i = 0; i < fields.length; i++) {
            simpleFields[i] = simpleField(fields[i]);
        }
        ICondition c = QPI.notIn(simpleFields, values);
        list.add(c);
        return this;
    }

    public Criteria between(DBField property, Object value1, Object value2) {
        ICondition c = addCriterionBetween(property(property), DBOperator.BETWEEN, value1, value2);
        list.add(c);
        return this;
    }

    public Criteria notBetween(DBField property, Object value1, Object value2) {
        ICondition c = addCriterionBetween(property(property), DBOperator.NOT_BETWEEN, value1, value2);
        list.add(c);
        return this;
    }

    public Criteria like(DBField property, String value) {
        ICondition c = addCriterion(property(property), DBOperator.LIKE, value);
        list.add(c);
        return this;
    }

    public Criteria like(DBField property, String value, char escape) {
        ICondition c = addCriterion(property(property), DBOperator.LIKE_ESCAPE, value);
        if (c instanceof PartCondition) {
            ((PartCondition) c).setEscape(String.valueOf(escape));
        }
        list.add(c);
        return this;
    }

    public Criteria like(DBField property, String value, boolean escape) {
        ICondition c = addCriterion(property(property), (escape ? DBOperator.LIKE_ESCAPE : DBOperator.LIKE), value);
        list.add(c);
        return this;
    }

    public Criteria notLike(DBField property, String value) {
        ICondition c = addCriterion(property(property), DBOperator.NOT_LIKE, value);
        list.add(c);
        return this;
    }

    public Criteria notLike(DBField property, String value, char escape) {
        ICondition c = addCriterion(property(property), DBOperator.NOT_LIKE, value);
        if (c instanceof PartCondition) {
            ((PartCondition) c).setEscape(String.valueOf(escape));
        }
        list.add(c);
        return this;
    }

    public Criteria notLike(DBField property, String value, boolean escape) {
        ICondition c = addCriterion(property(property), (escape ? DBOperator.NOT_LIKE_ESCAPE : DBOperator.NOT_LIKE), value);
        list.add(c);
        return this;
    }

    //以下是simpleField条件
    public Criteria isNull(SimpleField property) {
        ICondition c = property.isNull();
        list.add(c);
        return this;
    }

    public Criteria isNotNull(SimpleField property) {
        ICondition c = property.isNotNull();
        list.add(c);
        return this;
    }

    public Criteria eq(SimpleField property, Object value) {
        ICondition c = property.eq(value);
        list.add(c);
        return this;
    }

    public Criteria ne(SimpleField property, Object value) {
        ICondition c = property.ne(value);
        list.add(c);
        return this;
    }

    public Criteria gt(SimpleField property, Object value) {
        ICondition c = property.gt(value);
        list.add(c);
        return this;
    }

    public Criteria ge(SimpleField property, Object value) {
        ICondition c = property.ge(value);
        list.add(c);
        return this;
    }

    public Criteria lt(SimpleField property, Object value) {
        ICondition c = property.lt(value);
        list.add(c);
        return this;
    }

    public Criteria le(SimpleField property, Object value) {
        ICondition c = property.le(value);
        list.add(c);
        return this;
    }

    public Criteria in(SimpleField property, Object value) {
        // 对于空集合的IN条件为永假
        ICondition c = property.in(value);
        list.add(c);
        return this;
    }

    public Criteria in(SimpleField property, Collection<?> values) {
        // 对于空集合的IN条件为永假
        ICondition c = property.in(values);
        list.add(c);
        return this;
    }

    public Criteria in(SimpleField property, Object... values) {
        // 对于空集合的IN条件为永假
        ICondition c = property.in(values);
        list.add(c);
        return this;
    }

    /**
     * @param fields
     * @param values
     * @return
     */
    public Criteria in(SimpleField[] fields, Collection<?> values) {
        ICondition c = QPI.in(fields, values);
        list.add(c);
        return this;
    }

    public Criteria notIn(SimpleField property, Object value) {
        // 对于空集合的IN条件为永真
        ICondition c = property.notIn(value);
        list.add(c);
        return this;
    }

    public Criteria notIn(SimpleField property, Collection<?> values) {
        // 对于空集合的NOT IN条件为永真
        ICondition c = property.notIn(values);
        list.add(c);
        return this;
    }

    public Criteria notIn(SimpleField property, Object... values) {
        // 对于空集合的NOT IN条件为永真
        ICondition c = property.notIn(values);
        list.add(c);
        return this;
    }

    /**
     * @param fields
     * @param values
     * @return
     */
    public Criteria notIn(SimpleField[] fields, Collection<?> values) {
        ICondition c = QPI.notIn(fields, values);
        list.add(c);
        return this;
    }

    public Criteria between(SimpleField property, Object value1, Object value2) {
        ICondition c = property.between(value1, value2);
        list.add(c);
        return this;
    }

    public Criteria notBetween(SimpleField property, Object value1, Object value2) {
        ICondition c = property.notBetween(value1, value2);
        list.add(c);
        return this;
    }

    public Criteria like(SimpleField property, String value) {
        ICondition c = property.like(value);
        list.add(c);
        return this;
    }

    public Criteria like(SimpleField property, String value, char escape) {
        ICondition c = property.like(value, escape);
        list.add(c);
        return this;
    }

    public Criteria like(SimpleField property, String value, boolean escape) {
        ICondition c = property.like(value, escape);
        list.add(c);
        return this;
    }

    public Criteria notLike(SimpleField property, String value) {
        ICondition c = property.notLike(value);
        list.add(c);
        return this;
    }

    public Criteria notLike(SimpleField property, String value, char escape) {
        ICondition c = property.notLike(value, escape);
        list.add(c);
        return this;
    }

    public Criteria notLike(SimpleField property, String value, boolean escape) {
        ICondition c = property.notLike(value, escape);
        list.add(c);
        return this;
    }

    //以下是连接条件

    public Criteria and() {
        ICondition c = QPI.and();
        list.add(c);
        return this;
    }

    public Criteria or() {
        ICondition c = QPI.or();
        list.add(c);
        return this;
    }

    public Criteria leftP() {
        ICondition c = QPI.leftP();
        list.add(c);
        return this;
    }

    public Criteria rightP() {
        ICondition c = QPI.rightP();
        list.add(c);
        return this;
    }

    public Criteria exists(Example example) {
        list.add(QPI.exists(example));
        return this;
    }

    public Criteria notExists(Example example) {
        list.add(QPI.notExists(example));
        return this;
    }

    /**
     * 返回到temp对象中
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T backMain(Class<T> clazz) {
        if (temp != null && clazz.isAssignableFrom(temp.getClass())) {
            return (T) temp;
        }
        return null;
    }

    public List<ICondition> getList() {
        return list;
    }

    public void setList(List<ICondition> list) {
        this.list = list;
    }
}